<?php namespace app\service\controller;
use think\Controller;
use think\Db;
require("../vendor/wechat/wxBizDataCrypt.php");

class Wechat extends Controller{
	
	private $url = [
		'getOpenid'	=> 'https://api.weixin.qq.com/sns/jscode2session?appid={APPID}&secret={SECRET}&js_code={JSCODE}&grant_type=authorization_code',	//获取openid
		'getUserInfo'		=> '',	//获取用户基础信息
		'getQrcode'	=>	'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=',							//生成小程序二维码(可接受页面参数较短，生成个数不受限)
		'getAccessToken' => 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={APPID}&secret={SECRET}',					//获取accessToken
		'unifiedorder' => 'https://api.mch.weixin.qq.com/pay/unifiedorder',	//微信支付(统一下单)
        'profitsharing' => 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing',	//请求单次分账
        'refund' => 'https://api.mch.weixin.qq.com/secapi/pay/refund',	//退款
        'profitsharingaddreceiver' => 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver',	//添加分账接收方
	];
	
	static private $cacheServiceObj = '';	//缓存服务句柄
	static private $config = '';			//配置项
	
	public function __construct(){
		if(empty(self::$cacheServiceObj)) self::$cacheServiceObj = controller("service/Cache");							//缓存服务句柄
		if(empty(self::$config)) {
			if( empty(input('register_type')) ){
				self::$config = config('WECHAT_MIN');
			}else{
				if( !empty(input('register_type')) && input('register_type') == 2 ) self::$config = config('WECHAT_MIN');
				if( !empty(input('register_type')) && input('register_type') == 6 ) self::$config = config('WECHAT_MIN2');
			}
		}													//配置项
	}

    /**
     *退款
	 * @param	string	$wechatPayCof			商店微信支付配置项
	 * @param	string	$transaction_id			微信支付订单号transaction_id
	 * @param	string	$total_fee				订单金额
	 * @param	string	$refund_fee				退款金额
	 * @param	string	$refund_desc			退款原因
	 * @param	string	$orderCode				商户退款单号(唯一)
     * 
     */
    public function refund($wechatPayCof, $transaction_id, $total_fee, $refund_fee, $refund_desc, $orderCode){
        // 文档地址:https://pay.weixin.qq.com/doc/v2/merchant/4011937424
        $payConfig = config("WECHAT_PAY");


        $params['appid'] = self::$config['APPID'];						//APPID
        $params['mch_id'] = $wechatPayCof['MCH_ID'];					//微信支付商户号
        $params['nonce_str'] = $this -> createStr()['data'];			//随机字符串
        $params['out_refund_no'] = $orderCode;							//商户退款单号(唯一)
        $params['refund_fee'] = $refund_fee * 100;						//退款金额
        $params['total_fee'] = $total_fee * 100;						//订单金额
        $params['transaction_id'] = $transaction_id;					//微信订单号

        $params['refund_desc'] = $refund_desc;						//退款原因
        $params['notify_url'] = $payConfig['REFUND_URL'];			//退款结果通知url

        $sign = $this -> getPaySign($params, $wechatPayCof);
        if($sign['code'] != '1'){
			$outArr['code'] = '50003';
			
			$log['return'] = $outArr;
			$log['endDate'] = date("Y-m-d H:i:s", time());
			controller("service/log") -> logs($log, 'wxrefund.txt');
			return $outArr;
		}

        $params['sign'] = $sign['data'];		//签名
        $paramsXml = arrayToXml2($params);

		$url = $this -> url['refund'];

        $res = requestPost($url, $paramsXml, null, true);
		controller("service/log") -> logs($params, 'wxrefund.txt');
		controller("service/log") -> logs($res, 'wxrefund.txt');
        $res = xmlToArray($res);
        if($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS'){
			$outArr['code'] = '50003';
			$outArr['msg'] = $res;
			
			$log['return'] = $res;
			$log['endDate'] = date("Y-m-d H:i:s", time());
			return $outArr;
		}
		$outArr['code'] = '1';
		$outArr['info'] = $res;
		return $outArr;
    }

    /**
	*添加分账接收方
	*/
	public function profitsharingaddreceiver($wechatPayCof, $receiver){

		$payConfig = config("WECHAT_PAY");

        $params['appid'] = self::$config['APPID'];				//APPID
		$params['mch_id'] = $wechatPayCof['MCH_ID'];	//微信支付商户号
		$params['nonce_str'] = $this -> createStr()['data'];			//随机字符串
		$params['receiver'] = json_encode($receiver);						//分账接收方列表
		$sign = $this -> getPaySign2($params, $wechatPayCof);			//签名（）
		$sign_type = 'HMAC-SHA256';				//签名类型，目前只支持HMAC-SHA256

		$params['sign'] = $sign['data'];		//签名
		$payParamXml = arrayToXml($params);

		$url = $this -> url['profitsharingaddreceiver'];
		
		$res = requestPost($url, $payParamXml, null, true);
		$res = xmlToArray($res);
		controller("service/Log") -> logs($res, 'profitsharing1.txt');
		return $res;
	}

	/**
	*单次分账返佣
	*/
	public function profitsharing($wechatPayCof, $transaction_id, $receivers, $order_id){
		// 文档地址:https://pay.weixin.qq.com/doc/v2/merchant/4011941670

		$payConfig = config("WECHAT_PAY");

		// 添加分账接收方
		$receiver['type'] = $receivers[0]['type'];
		$receiver['account'] = $receivers[0]['account'];
		$receiver['relation_type'] = 'USER';
		$re = $this -> profitsharingaddreceiver($wechatPayCof, $receiver);
		if( $re['return_code'] != 'SUCCESS' || $re['result_code'] != 'SUCCESS' ) {
			$outArr['code'] = '-1';
			$outArr['info'] = $re;
			return $outArr;
		}


		// 分账
        $params['appid'] = self::$config['APPID'];				//APPID
		$params['mch_id'] = $wechatPayCof['MCH_ID'];	//微信支付商户号
		$params['nonce_str'] = $this -> createStr()['data'];			//随机字符串
		$params['out_order_no'] = time().rand(1000, 9999);		//商户分账单号
		$params['transaction_id'] = $transaction_id;			//微信订单号
		$params['receivers'] = json_encode($receivers);						//分账接收方列表
		$sign = $this -> getPaySign2($params, $wechatPayCof);			//签名（）
		$sign_type = 'HMAC-SHA256';				//签名类型，目前只支持HMAC-SHA256

		$params['sign'] = $sign['data'];		//签名
		$payParamXml = arrayToXml($params);

		controller("service/Log") -> logs($params, 'profitsharing2.txt');
		$url = $this -> url['profitsharing'];
		
		$res = requestPost($url, $payParamXml, null, true);
		$res = xmlToArray($res);
		controller("service/Log") -> logs($res, 'profitsharing2.txt');
		if($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS'){
			$o = Db::table("gd_wechat_separate_fail") -> where(['order_id'=>$order_id]) -> find();
			if( empty($o) ){
				$wechatSeparateFail['order_id'] = $order_id;
				$wechatSeparateFail['out_order_no'] = $params['out_order_no'];
				$wechatSeparateFail['date'] = date("Y-m-d H:i:s", time());
				$wechatSeparateFail['desc'] = $res['err_code_des'];
				Db::table("gd_wechat_separate_fail") -> insertGetId($wechatSeparateFail);
			}
			
			$outArr['code'] = '50003';
			$outArr['info'] = $res;
			return $outArr;
		}
		$outArr['code'] = '1';
		$outArr['info'] = $res;
		return $outArr;
	}

	
	/**
	 * 微信支付(统一下单)
	 * @param	string	$body			商品描述
	 * @param	string	$totalFee		支付金额(分)
	 * @param	string	$openid			openid
	 * @param	string	$attach			自定义参数(支付成功原样返回)
	 * @param	string	$tradeType		交易类型(JSAPI、MWEB)
	 * @param	string	$sceneInfo		场景信息(安卓:{"h5_info":{"type": "场景类型","app_name": "应用名","package_name": "包名"}}，IOS：{"h5_info":{"type": "场景类型",  // "app_name": "应用名","bundle_id": "bundle_id"}}，WAP网站应用：{"h5_info":{"type": "场景类型",  // "wap_url": "WAP网站URL地址","wap_name": "WAP 网站名"}})
	 * @param	string	$wechatPayCof	商家的微信支付配置项
	 * @param	string	$profit_sharing	是否需要分账（Y：是，N：否）
	 */
	public function unifiedOrder($body = '', $totalFee = '', $openid = '', $attach = [], $userInfo = [], $weChatConfig = [], $tradeType = 'JSAPI', $sceneInfo = '', $wechatPayCof = [], $profit_sharing = ''){
		if( empty($body) || empty($totalFee) || empty($openid) ){
			$outArr['code'] = '40004';
			return $outArr;
		}
		
		if( !empty($weChatConfig) ) self::$config = $weChatConfig;
		
		$payConfig = config("WECHAT_PAY");									//总平台的微信支付配置项
		$nonceStr = $this -> createStr();									//随机字符串
		$outTradeNo = $this -> createOrderNo();								//订单号
		$totalFee = $totalFee;
		
		if($nonceStr['code'] != '1' || $outTradeNo['code'] != '1'){
			$outArr['code'] = '50003';
			return $outArr;
		}

		$attach['out_trade_no'] = $outTradeNo['info'];
//		参数
		$payParam['appid'] = self::$config['APPID'];									//appid
		$payParam['mch_id'] = !empty($wechatPayCof) ? $wechatPayCof['MCH_ID'] : $payConfig['MCH_ID'];	//商户号
		$payParam['nonce_str'] = $nonceStr['data'];										//随机字符串
		$payParam['body'] = $body;														//商品描述
		$payParam['out_trade_no'] = $outTradeNo['info'];								//订单号
		if( isset($profit_sharing) && !empty($profit_sharing) ) $payParam['profit_sharing'] = $profit_sharing;
		$payParam['total_fee'] = $totalFee;												//下单金额(分)
		$payParam['spbill_create_ip'] = getIP();										//调用者ip
		$payParam['notify_url'] = $payConfig['NOTIFY_URL'];								//支付结果回调地址
		$payParam['trade_type'] = $tradeType;											//交易类型
		$payParam['openid'] = $openid;													//openid
		$payParam['attach'] = json_encode($attach, JSON_UNESCAPED_UNICODE);				//自定义参数
		if( $tradeType == 'MWEB' && !empty($sceneInfo) ) $payParam['scene_info'] = $sceneInfo;
		
		$sign = $this -> getPaySign($payParam, $wechatPayCof);

		
		if($sign['code'] != '1'){
			$outArr['code'] = '50003';
			
			$log['return'] = $outArr;
			$log['endDate'] = date("Y-m-d H:i:s", time());
			controller("service/log") -> logs($log, 'unifiedOrder.txt');
			return $outArr;
		}
		$payParam['sign'] = $sign['data'];		//签名
		$payParamXml = arrayToXml($payParam);
		
		$url = $this -> url['unifiedorder'];
		
		$log['url'] = $url;
		$log['param'] = $payParamXml;
		controller("service/Log") -> logs($log, 'unifiedOrder.txt');
		
		$res = requestPost($url, $payParamXml);
//		var_dump($res);
		$res = xmlToArray($res);
		
		if($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS'){
			$outArr['code'] = '50003';
			$outArr['msg'] = $res;
			
			$log['return'] = $res;
			$log['endDate'] = date("Y-m-d H:i:s", time());
			controller("service/log") -> logs($log, 'unifiedOrder.txt');
			return $outArr;
		}
		
		$payType = $attach['payType'];
		$orderId = 0;
		$identifyId = 0;
		$auctionId = 0;
		$auctionSignupId = 0;
		if( in_array($payType, [1, 6]) ) $orderId = $attach['orderId'];			//下单支付
		if( $payType == 2 ) $identifyId = $attach['identifyId'];	//鉴定支付
		if( $payType == 3 ) {										//拍卖保证金支付
			$auctionId = $attach['auctionId'];
			$auctionSignupId = $attach['auctionSignupId'];
		}
		if( $payType == 4 ) {										//拍卖尾款支付
			$auctionId = $attach['auctionId'];
			$auctionSignupId = $attach['auctionSignupId'];
		}
		
		$paymentRecords['pay_type'] = 'WECHAT_JSAPI';
		$paymentRecords['pay_code'] = '';
		$paymentRecords['pay_price'] = $totalFee / 100;
		$paymentRecords['pay_date'] = '0000-00-00 00:00:00';
		$paymentRecords['prepay_id'] = $res['prepay_id'];
		$paymentRecords['out_trade_no'] = $payParam['out_trade_no'];
		$paymentRecords['nonce_str'] = $payParam['nonce_str'];
		$paymentRecords['state'] = $payType;
		$paymentRecords['order_id'] = $orderId;
		$paymentRecords['identify_id'] = $identifyId;
		$paymentRecords['auction_id'] = $auctionId;
		$paymentRecords['auction_signup_id'] = $auctionSignupId;
		$paymentRecords['create_user_id'] = $userInfo['id'];
		$paymentRecords['create_user_name'] = $userInfo['nickname'];
		$paymentRecords['create_user_type'] = $userInfo['type'];
		$paymentRecords['create_date'] = date("Y-m-d H:i:s", time());
		$paymentRecords['refund_create_date'] = '0000-00-00 00:00:00';
		$paymentRecords['refund_date'] = '0000-00-00 00:00:00';
		
		$addRes = Db::table("gd_payment_records") -> insertGetId($paymentRecords);
		
		$outArr['code'] = '1';
		$outArr['info'] = $res;
		return $outArr;
	}

    /**
     * 分账签名（HMAC-SHA256签名）
     */
    private function getPaySign2($params = null, $wechatPayCof = []){
        if(is_null($params)){
            $outArr['code'] = '40004';
        }else{

			$payKey = !empty($wechatPayCof) ? $wechatPayCof['API_V2'] : config("WECHAT_PAY")['API_V2'];

//			将参数数组按照参数名ASCII码从小到大排序
            ksort($params);
            $newArr = [];
            foreach ($params as $key => $item) {
//	        	剔除参数值为空的参数
                if (!empty($item)) {
                    $newArr[] = $key . '=' . $item;
                }
            }
            $stringA = implode("&", $newArr);
            $stringSignTemp = $stringA . "&key=" . $payKey;        //拼接key,key是在商户平台API安全里自己设置的

            $stringSignTemp = hash_hmac("sha256", $stringSignTemp, $payKey);		//将字符串进行MD5加密
            $sign = strtoupper($stringSignTemp);		//将所有字符转换为大写

            $outArr['code'] = '1';
            $outArr['data'] = $sign;
        }
        return $outArr;
    }

	/**
	 * 统一下单签名
	 */
	private function getPaySign($params = null, $wechatPayCof = []){
		if(is_null($params)){
			$outArr['code'] = '40004';
		}else{

			$payKey = !empty($wechatPayCof) ? $wechatPayCof['API_V2'] : config("WECHAT_PAY")['API_V2'];

//			将参数数组按照参数名ASCII码从小到大排序
			ksort($params);
			$newArr = [];
	        foreach ($params as $key => $item) {
//	        	剔除参数值为空的参数
	            if (!empty($item)) {
	                $newArr[] = $key . '=' . $item;
	            }
	        }
	        $stringA = implode("&", $newArr);
	        $stringSignTemp = $stringA . "&key=" . $payKey;        //拼接key,key是在商户平台API安全里自己设置的
	        
	        $stringSignTemp = MD5($stringSignTemp);		//将字符串进行MD5加密
	        $sign = strtoupper($stringSignTemp);		//将所有字符转换为大写
	        
			$outArr['code'] = '1';
			$outArr['data'] = $sign;
		}
		return $outArr;
	}
	
	/**
	 * 生成订单号
	 */
	private function createOrderNo(){
		$outArr['code'] = '1';
		$outArr['info'] = date("YmdHis") . rand(1000, 99999999);
		return $outArr;
	}
	
	/**
	 * 生成随机字符串
	 */
	private function createStr(){
		$str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&+-*/_|';
		$len = strlen($str) -1;
		$length = strlen($str);
		$randStr = time();
		for ($i = 0; $i < $length; $i++) {
			$num = mt_rand(0,$len);
			$randStr .= $str[$num];
		}
		$outArr['code'] = '1';
		$outArr['data'] = md5($randStr);
		return $outArr;
	}
	
	
	/**
	 * 生成二维码
	 * @param	array	$param	自定义参数
	 * @return	array	文件路径
	 */
	public function createQrcode($param = []){
		$accessToken = $this -> getAccessToken();
		if($accessToken['code'] != '0'){
			$outArr['code'] = $accessToken['code'];
			return $outArr;
		}
		
		$url = $this -> url['getQrcode'];
		$url = $url . $accessToken['data'];
		
		$data['scene'] = isset($param['scene']) ? $param['scene'] : '';
		$data['page'] = isset($param['page']) ? $param['page'] : '';
//		$data['is_hyaline'] = true;
		
//		p($url);
//		p($data);
//		die;
		
//		微信返回二维码的二进制内容
		$re = requestPost($url, json_encode($data));
		
		//保存地址
//      $dirPath = 'upload/wechat/mini/' . date("Y-m-d");   //必须是项目所在的绝对路径
//  	if(!is_dir($dirPath)){
//			$mkdirRe = mkdir($dirPath,0777,TRUE);
//		}
        
        
        //要生成的图片名字
//      $filename = date("YmdHis").rand(100, 9999) . ".png"; //新图片名称
//      $newFilePath = $dirPath . '/' . $filename;
//      $newFile = fopen($newFilePath, "w"); //打开文件准备写入
//      fwrite($newFile, $re); //写入二进制流到文件
//      fclose($newFile); //关闭文件
		
		$outArr['code'] = '1';
//		$outArr['info'] = $newFilePath;
		$outArr['info'] = $re;
		return $outArr;
	}
	
	/**
	 * 获取accessToken
	 */
	private function getAccessToken(){
		$wechatAccessTokenName = 'wechatAccessToken'.self::$config['APPID'];
		$accessToken = self::$cacheServiceObj -> getCache($wechatAccessTokenName);
		if(empty($accessToken)){
//			var_dump('走链接');
			$url = $this -> url['getAccessToken'];
		
			$url = str_replace('{APPID}', self::$config['APPID'], $url);
			$url = str_replace('{SECRET}', self::$config['SECRET'], $url);
			
			$re = requestGet($url);
			$re = json_decode($re, true);
			if(isset($re['access_token']) && !empty($re['access_token'])){
				self::$cacheServiceObj -> setCache($wechatAccessTokenName, '', $re['access_token'], 7000);
				$outArr['code'] = 0;
				$outArr['data'] = $re['access_token'];
			}else{
				$outArr['code'] = '50003';
			}
		}else{
//			var_dump('走缓存');
			$outArr['code'] = 0;
			$outArr['data'] = $accessToken;
		}
		return $outArr;
	}
	
	/**
	 * 解密手机号
	 */
	public function decodeMobile($iv, $encryptedData, $sessionKey){
		$pc = new \WXBizDataCrypt(self::$config['APPID'], $sessionKey);
		$errCode = $pc -> decryptData($encryptedData, $iv, $data );
		
		if ($errCode == 0) {
			$outArr['code'] = 0;
		    $outArr['data'] = json_decode($data, true);
		} else {
			$outArr['code'] = -43001;
			$outArr['data'] = $errCode;
		}
		$log['model'] = 'decodeMobile';
		$log['in']['iv'] = $iv;
		$log['in']['encryptedData'] = $encryptedData;
		$log['in']['sessionKey'] = $sessionKey;
		$log['return'] = $errCode;
		controller("service/Log") -> logs($log, 'wechat.txt');
		return $outArr;
	}
	
	/**
	 * 获取openid
	 * @param	string	$jsCode	jsApi获取到的登录code
	 */
	public function getOpenid($jsCode, $iv, $encryptedData){
		$url = $this -> url['getOpenid'];
		
		$url = str_replace('{APPID}', self::$config['APPID'], $url);
		$url = str_replace('{SECRET}', self::$config['SECRET'], $url);
		$url = str_replace('{JSCODE}', trim($jsCode), $url);
		
		$re = requestGet($url);
		$re = json_decode($re, true);
		
//		if(isset($re['session_key']) && isset($re['openid'])){
//			$outArr = $this -> decodeMobile($iv, $encryptedData, $re['session_key']);
//		}else{
//			$outArr['code'] = '50003';
//		}
		
		if(isset($re['session_key']) && isset($re['openid'])){
			$outArr['code'] = 0;
			$outArr['sessionKey'] = $re['session_key'];
			$outArr['openid'] = $re['openid'];
		}else{
			$outArr['code'] = -43001;
			$outArr['data'] = $re;
		}
		return $outArr;
	}

	/**
	 * 解密退款回调数据
	 */
	public function decodeRefundCallback($reqInfo, $wechat_pay_api_v2){
		// 解密
		$key = md5($wechat_pay_api_v2);
	    $reqInfo = base64_decode($reqInfo);
	    return openssl_decrypt($reqInfo, 'AES-256-ECB', $key, OPENSSL_RAW_DATA);
	}
	
}

?>